package com.mixsmart.core.statement.filter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.mixsmart.utils.StringUtils;

/**
 * 根据参数，过滤SQL语句中的可选条件（语句）<br />
 * <b>不支持可选条件间的嵌套，既：中括号中不能再有中括号</b>；
 * <p>通过参数(Map对象类型)的key值，来判断sql语句中是否有该key有关的可选条件（语句）；</p>
 * <p>如果有，则sql语句中的可选条件（语句）生效，否则可选条件（语句）从sql语句中过滤删除；</p>
 * <p>可选条件（语句）用中括号“[]”括起来；如：
 * select * form t_table t [where t.table_name=:name] order by t.seq_num asc</p>
 * <p>其中[where t.table_name=:name]为可选条件，当参数的key中有name变量时，该条件就变为可用(生效)，否则该条件从sql语句中过滤删除。</p>
 * @author lmq
 * @version 1.0
 * @since JDK版本大于等于1.6
 * 2016年1月14日
 */
public class SQLConditionFilter implements IStatementFilter {

	private String sql;
	
	private HashMap<String,Object> params;
	
	@SuppressWarnings("unchecked")
	public SQLConditionFilter(String sql, Map<String,Object> params) {
		this.sql = sql;
		if(null != params && params.size()>0) {
			if(params instanceof HashMap) {
			    this.params = (HashMap<String, Object>) ((HashMap<String, Object>)params).clone();
			}
		}
		this.initParamBySql();
	}
	
	@Override
	public String getStatement() {
		return sql;
	}
	
	@Override
	public HashMap<String, Object> getParams() {
		return params;
	}
	
	/**
	 * 重新初始化参数
	 * 根据SQL语句中的出现的变量，重新初始化params参数；<br />
	 * 避免sql语句中没有的变量出现在params变量参数里面，防止SQL语句报错
	 */
	private boolean initParamBySql() {
		boolean is = false;
		if(StringUtils.isNotEmpty(this.sql)) {
			//获取SQL语句中的变量
			String regex = "(:\\w+)";
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(this.sql);
			Set<String> sqlInVars = new HashSet<String>();
			while(matcher.find()) {
				String param = matcher.group();
				if(StringUtils.isNotEmpty(param)) {
					param = param.substring(1,param.length());
					sqlInVars.add(param);
				}  
			}
			//处理（删除）SQL语句中未出现的变量
			if(null != this.params && !this.params.isEmpty() && !sqlInVars.isEmpty()) {
				Set<String> keys = this.params.keySet();
				boolean isExist = false;
				Set<String> removeKeySet = new HashSet<String>();
				for (String key : keys) {
					isExist = false;
					for(String sqlKey:sqlInVars) {
						if(key.equals(sqlKey)) {
							isExist = true;
							break;
						}
					}
					if(!isExist)
						removeKeySet.add(key);
				}//end for
				if(removeKeySet.size()>0) {
					for (String key : removeKeySet)
						this.params.remove(key);
				}
				removeKeySet = null;
			} else if(sqlInVars.isEmpty()) { //sql语句中没有变量出现时，清空传入的参数
				this.params = null;
			}
			is = true;
		}
		return is;
	}
	
	/**
	 * 获取参数key值
	 * @return 返回参数key集合
	 */
	private List<String> getParamKey() {
		List<String> paramKeys = null;
		if(null != this.params && !this.params.isEmpty()) {
			paramKeys = new ArrayList<String>();
			Set<String> sets = this.params.keySet();
			for (String key : sets) {
				Object value = this.params.get(key);
				if(null != value && StringUtils.isNotEmpty(StringUtils.handNull(value))) {
					if(value.getClass().isArray()) {
						Object[] values = (Object[])value;
						if(values.length>1) {
							paramKeys.add(key);
						} else if(null != values[0] && StringUtils.isNotEmpty(StringUtils.handNull(values[0]))) {
							paramKeys.add(key);
						}
					} else {
						paramKeys.add(key);
					}
				}//if
			}//for
		}
		return paramKeys;
	}
	
	/**
	 * 获取可选条件(语句)
	 * @return 返回可选条件(语句)集合
	 */
	protected List<String> getOptionalSql() {
		List<String> optionals = null;
		if(StringUtils.isNotEmpty(this.sql)) {
			optionals = new ArrayList<String>();
			String regex = "(?<=\\[)[^\\[]+(?=\\])";
			Pattern pattern = Pattern.compile(regex);
			Matcher matcher = pattern.matcher(this.sql);
			while(matcher.find()) {
				optionals.add(matcher.group());
			}
		}
		return (optionals != null && !optionals.isEmpty())?optionals:null;
	}
	
	/**
	 * 根据参数变量过滤可选条件(语句)
	 * @param optionals 可选条件（语句）
	 * @param varParams 参数
	 * @return 返回过滤后的条件（语句）
	 */
	private List<String> filterOptionalsSql(List<String> optionals,List<String> varParams) {
		List<String> hasSqls = new ArrayList<String>();
		for (String optional : optionals) {
			for (String varParam : varParams) {
				if(optional.matches(".*?:"+varParam+"[\\s*|\\]|%|']*?")) {
					hasSqls.add(optional);
				}
			}
		}
		return (hasSqls != null && !hasSqls.isEmpty())?hasSqls:null;
	}
	
	@Override
	public void filter() {
		List<String> optionals = this.getOptionalSql();
		if(null != optionals && optionals.size()>0) {
			List<String> paramKeys = this.getParamKey();
			//更加参数，满足条件的可选条件(语句)生效
			if(null != paramKeys && paramKeys.size()>0) {
				List<String> hasSqls = this.filterOptionalsSql(optionals, paramKeys);
				if(null != hasSqls && hasSqls.size()>0) {
					for (String hasSql : hasSqls) {
						this.sql = this.sql.replace("["+hasSql+"]", hasSql);
					}
				}
				hasSqls = null;
			}
			paramKeys = null;
		}
		optionals = null;
		//替换（删除）所有不满足条件的可选条件（语句）
		this.sql = this.sql.replaceAll("\\[([^\\]]+)\\]", "");
		//like处理
		if(this.sql.indexOf("%")>-1 && null != this.params && this.params.size()>0) {
			Set<String> sets = this.params.keySet();
			Map<String, Object> newParam = new HashMap<String, Object>();
			Pattern pattern = null;
			Matcher matcher = null;
			Pattern patternUnlike = null;
			Matcher matcherUnlike = null;
			
			String group = null;
			String paramValue = null;
			String groupReplaceValue = null;
			String likeName = null;
			String newKey = null;
			boolean isMatcher = false;
			for (String key : sets) {
				String regex = "('%?:"+key+"%?')";
				pattern = Pattern.compile(regex);
				matcher = pattern.matcher(this.sql);
				int count = 0;
				isMatcher = false;
				while(matcher.find()) {
					count++;
					isMatcher = true;
					group = matcher.group();
					paramValue = this.params.get(key).toString();
					groupReplaceValue = group.replace(":"+key, paramValue);
					if(count>1) {
						likeName = ":"+key+count;
						newKey = key+count;
					} else {
						String regexUnlike = "([^%]:"+key+"[^%]?\\s+)";
						patternUnlike = Pattern.compile(regexUnlike);
						matcherUnlike = patternUnlike.matcher(this.sql);
						if(matcherUnlike.find()) {
							likeName = ":"+key+count;
							newKey = key+count;
						} else {
							likeName = ":"+key;
							newKey = key;
						}
					}
					this.sql = this.sql.replaceFirst(group, likeName);
					groupReplaceValue = groupReplaceValue.replaceAll("'", "");
					newParam.put(newKey, groupReplaceValue);
				}//end while
				if(!isMatcher) {
					newParam.put(key, this.params.get(key));
				}
			}//end for
			this.params.putAll(newParam);
		}//end if
	}
}
